package com.by.rpc.client;

import com.by.log.Logger;
import com.by.log.LoggerFactory;
import com.by.rpc.annotation.RpcReference;
import com.by.rpc.config.RpcProperties;
import com.by.rpc.dto.ServiceInstance;
import com.by.rpc.factory.RpcProxyFactory;
import com.by.rpc.registry.ServiceRegistry;
import com.by.rpc.util.KeyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.NonNull;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * RPC引用的Bean后置处理器，用于注入远程服务代理
 */
public class RpcReferenceBeanPostProcessor implements BeanPostProcessor {
    private static final Logger logger = LoggerFactory.getLogger(RpcReferenceBeanPostProcessor.class);

    private final RpcProperties rpcProperties;
    private final ObjectProvider<ServiceRegistry> serviceRegistryProvider;

    /**
     * 构造函数使用ObjectProvider延迟注入ServiceRegistry，避免提前初始化
     */
    public RpcReferenceBeanPostProcessor(RpcProperties rpcProperties,
                                         ObjectProvider<ServiceRegistry> serviceRegistryProvider) {
        this.rpcProperties = rpcProperties;
        this.serviceRegistryProvider = serviceRegistryProvider;
    }

    @Override
    public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();

        for (Field field : beanClass.getDeclaredFields()) {
            if (field.isAnnotationPresent(RpcReference.class)) {
                RpcReference reference = field.getAnnotation(RpcReference.class);
                Class<?> interfaceClass = field.getType();

                if (reference.interfaceClass() != void.class) {
                    interfaceClass = reference.interfaceClass();
                }

                if (!interfaceClass.isInterface()) {
                    throw new RuntimeException("@RpcReference can only be used on interface fields");
                }

                String interfaceName = interfaceClass.getName();

                // 核心改动：在注入代理时，将服务信息注册到 HealthChecker
                // 此时并不执行服务发现，只是记录下来
                ServiceRegistry serviceRegistry = getServiceRegistry();
                String serviceName = serviceRegistry.getServernameByInterface(interfaceName);
                RpcReferenceHealthChecker.registerRpcService(beanName, serviceName, interfaceName, reference.version(), reference.group());

                // 创建延迟加载代理
                Object proxy = createLazyProxy(interfaceClass, reference, serviceName);

                field.setAccessible(true);
                try {
                    field.set(bean, proxy);
                    logger.info("Injected lazy RPC reference for {}.{} with interface:{}",
                            beanClass.getName(), field.getName(), interfaceName);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Failed to inject RPC reference: " + e.getMessage(), e);
                }
            }
        }
        return bean;
    }

    /**
     * 获取ServiceRegistry实例，主要供HealthChecker使用
     *
     * @return ServiceRegistry实例，如果不可用则返回null
     */
    public ServiceRegistry getServiceRegistryForCheck() {
        return serviceRegistryProvider.getIfAvailable();
    }

    private ServiceRegistry getServiceRegistry() {
        return serviceRegistryProvider.getIfAvailable(() -> {
            throw new RuntimeException("No ServiceRegistry bean available in the context");
        });
    }

    /**
     * 创建延迟加载代理对象
     */
    private Object createLazyProxy(Class<?> interfaceClass, RpcReference reference, String serviceName) {
        return Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class[]{interfaceClass},
                new LazyInvocationHandler(interfaceClass, reference, serviceName, rpcProperties, serviceRegistryProvider)
        );
    }

    @Override
    public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
        return bean;
    }

    /**
     * 延迟加载调用处理器（优化线程安全和服务发现逻辑）
     */
    private static class LazyInvocationHandler implements InvocationHandler {
        private final Class<?> interfaceClass;
        private final RpcReference reference;
        private final String serviceName;
        private final RpcProperties rpcProperties;
        private final ObjectProvider<ServiceRegistry> serviceRegistryProvider;
        private volatile Object realProxy;
        private volatile boolean initialized = false;

        public LazyInvocationHandler(Class<?> interfaceClass, RpcReference reference, String serviceName,
                                     RpcProperties rpcProperties, ObjectProvider<ServiceRegistry> serviceRegistryProvider) {
            this.interfaceClass = interfaceClass;
            this.reference = reference;
            this.serviceName = serviceName;
            this.rpcProperties = rpcProperties;
            this.serviceRegistryProvider = serviceRegistryProvider;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            }

            if (!initialized) {
                synchronized (this) {
                    if (!initialized) {
                        initializeProxy();
                        initialized = true;
                    }
                }
            }

            if (realProxy == null) {
                throw new RuntimeException("RPC service proxy initialization failed for: " + serviceName);
            }

            // 通过动态代理调用实际的代理方法
            return method.invoke(realProxy, args);
        }

        /**
         * 初始化代理对象（包含服务发现逻辑）
         */
        private void initializeProxy() {
            try {
                String interfaceName = interfaceClass.getName();
                ServiceRegistry serviceRegistry = serviceRegistryProvider.getIfAvailable(() -> {
                    throw new RuntimeException("ServiceRegistry is not available during proxy initialization");
                });

                String actualServiceName = serviceName != null
                        ? serviceName
                        : serviceRegistry.getServernameByInterface(interfaceName);

                if (actualServiceName == null) {
                    throw new RuntimeException("No service found for interface: " + interfaceName);
                }

                // 服务发现与负载均衡
                String serviceKey = KeyUtils.buildReferenceServiceKey(actualServiceName,
                        reference.version(),
                        reference.group());
                ServiceInstance instance = discoverServiceInstance(serviceRegistry, serviceKey);

                // 创建实际代理对象
                realProxy = new RpcProxyFactory(
                        interfaceClass,
                        rpcProperties.getServiceName(),
                        reference.version(),
                        reference.group(),
                        instance.getHost(),
                        instance.getPort()
                ).getProxy();

                logger.info("Successfully initialized RPC proxy for service: {}@{}:{}",
                        actualServiceName, instance.getHost(), instance.getPort());

            } catch (Exception e) {
                logger.error("Failed to initialize RPC proxy for service: " + serviceName, e);
            }
        }

        /**
         * 服务发现与实例选择
         */
        private ServiceInstance discoverServiceInstance(ServiceRegistry serviceRegistry, String serviceKey) {
            List<ServiceInstance> instances = serviceRegistry.discover(serviceKey);

            if (instances != null && !instances.isEmpty()) {
                int index = ThreadLocalRandom.current().nextInt(instances.size());
                return instances.get(index);
            }

            logger.warn("No service instances found for {}. Using default config: {}:{}",
                    serviceKey, rpcProperties.getServerHost(), rpcProperties.getServerPort());

            ServiceInstance defaultInstance = new ServiceInstance();
            defaultInstance.setHost(rpcProperties.getServerHost());
            defaultInstance.setPort(rpcProperties.getServerPort());
            return defaultInstance;
        }
    }
}